#include "ENCODER_Config.h"

/**
    ******************************************************************************
    * @brief  配置TIM8为编码器接口模式，实现正交编码器的位置和方向检测
    * @param  无
    * @retval 无
    * @note   核心原理：
    *         - 利用编码器输出的正交脉冲（A相和B相，相位差90°）
    *         - 定时器通过两个通道分别捕获A、B相脉冲，根据相位关系自动识别方向
    *         - 正转时计数器递增，反转时计数器递减，实现位置和方向的同步测量
    * @硬件依赖：
    *         - 编码器输出A相（ENCODER_PIN_A）和B相（ENCODER_PIN_B）信号
    *         - 信号需为3.3V电平，与STM32引脚兼容
    *         - 使用TIM8（高级定时器），支持编码器接口模式
    ******************************************************************************
*/
void Encoder_Config(void)
{
    // 1. 使能编码器输入引脚所在GPIO端口的时钟
    // ENCODER_CLK为宏定义（如RCC_APB2Periph_GPIOA），控制GPIO端口时钟
    RCC_APB2PeriphClockCmd(ENCODER_CLK, ENABLE);

    // 2. 配置编码器A相和B相输入引脚为上拉输入模式
    GPIO_InitTypeDef GPIO_InitStructure = {
        .GPIO_Mode  = GPIO_Mode_IPU,      				// 上拉输入模式：
                                                    	// - 内部上拉电阻确保无信号时引脚为高电平
                                                    	// - 适配编码器的推挽/开漏输出（默认高电平）
        .GPIO_Pin   = ENCODER_PIN_A | ENCODER_PIN_B,	// 编码器A相和B相引脚（如PA0和PA1）
        .GPIO_Speed = GPIO_Speed_50MHz,   				// 输入模式下速度配置无效，此处为默认值
    };
    GPIO_Init(ENCODER_PORT, &GPIO_InitStructure);  // 初始化编码器输入引脚（如GPIOA）

    // 3. 使能TIM8定时器时钟（高级定时器，挂载在APB2总线）
    RCC_APB2PeriphClockCmd(TIM_CLK, ENABLE);  // TIM_CLK为宏定义（如RCC_APB2Periph_TIM8）

    // 4. 配置TIM8时基单元（决定计数范围和时钟）
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruce = {
        .TIM_ClockDivision      = TIM_CKD_DIV1,			// 时钟分频：不分频，采样时钟=定时器时钟
                                                    	// 不影响编码器计数精度
        .TIM_CounterMode        = TIM_CounterMode_Up,	// 计数模式：向上计数（实际由编码器接口自动反转）
        .TIM_Period             = 65536 - 1,			// 自动重载值（ARR=65535）
                                                    	// 16位最大计数范围，减少溢出频率
        .TIM_Prescaler          = 1 - 1,				// 预分频器值（PSC=0）
                                                    	// 不分频，定时器时钟=APB2时钟（72MHz）
                                                    	// 确保高频脉冲不丢失
        .TIM_RepetitionCounter  = 0,					// 重复计数器：仅高级定时器有效，此处无用
    };
    TIM_TimeBaseInit(TIMx, &TIM_TimeBaseInitStruce);  // TIMx为宏定义（如TIM8）

    // 5. 配置TIM8通道1为输入捕获模式（用于A相脉冲）
    TIM_ICInitTypeDef TIM_ICInitStructure;                    		
    TIM_ICStructInit(&TIM_ICInitStructure);                    		// 填充默认值，避免未定义成员错误
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;           		// 通道1：连接编码器A相
    TIM_ICInitStructure.TIM_ICFilter = 0xF;                    		// 输入滤波：0xF（最大滤波等级）
                                                            		// 连续采样8次（STM32F10x），确认边沿有效
                                                            		// 滤除机械抖动或电磁干扰
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		// 上升沿捕获（A相上升沿触发）
    TIM_ICInit(TIMx, &TIM_ICInitStructure);                    		// 应用配置到通道1

    // 6. 配置TIM8通道2为输入捕获模式（用于B相脉冲）
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;           		// 通道2：连接编码器B相
    TIM_ICInitStructure.TIM_ICFilter = 0xF;                    		// 同通道1，最大滤波
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		// 上升沿捕获（B相上升沿触发）
    TIM_ICInit(TIMx, &TIM_ICInitStructure);                    		// 应用配置到通道2

    // 7. 配置编码器接口模式（核心函数）
    // 参数说明：
    // - TIMx：目标定时器（如TIM8）
    // - TIM_EncoderMode_TI12：使用TI1（通道1）和TI2（通道2）的信号进行编码
    // - 后两个参数：A相和B相的捕获极性（均为上升沿，由硬件自动处理反转）
    // 工作原理：
    // - 当A相超前B相90°时（正转），计数器递增
    // - 当B相超前A相90°时（反转），计数器递减
    // - 实现正交信号的方向识别和脉冲计数
    TIM_EncoderInterfaceConfig(TIMx, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

    // 8. 启动TIM8计数器，开始编码计数
    TIM_Cmd(TIMx, ENABLE);
}

/**
    ******************************************************************************
    * @brief  获取编码器在一段时间内的脉冲变化量（反映转速和方向）
    * @param  无
    * @retval int16_t：脉冲变化量（正值为正转，负值为反转）
    * @note   工作机制：
    *         1. 读取当前计数器值（TIMx->CNT）
    *         2. 将计数器清零，以便下次测量
    *         3. 返回读取的值，即两次调用之间的脉冲变化量
    * @应用场景：
    *         - 配合定时调用（如每10ms调用一次），计算转速：
    *           转速 = 脉冲变化量 / （编码器分辨率 × 时间）
    * @注意事项：
    *         - 若编码器转速过快，可能导致计数器溢出（16位范围-32768~32767）
    *         - 需在溢出前及时读取，或通过中断处理溢出
    *         - 返回值为相对变化量，非绝对位置
    ******************************************************************************
*/
int16_t Encoder_GetSpeed(void)
{
    int16_t Temp;  // 临时变量存储脉冲变化量
	
    Temp = TIM_GetCounter(TIMx);  // 读取当前计数器值（正转为正，反转为负）
    TIM_SetCounter(TIMx, 0);      // 计数器清零，准备下次测量

    return Temp;  // 返回这段时间内的脉冲变化量
}
